home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File: Interrupt_Routines.c Contains: Routines demonstrating how to write interrupt routines to deal with sound and I/O callbacks. Written by: Mark Cookson Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved. You may incorporate this Apple sample source code into your program(s) without restriction. This Apple sample source code has been provided "AS IS" and the responsibility for its operation is yours. You are not permitted to redistribute this Apple sample source code as "Apple sample source code" after having made changes. If you're going to re-distribute the source, we require that you make it clear in the source that the code was descended from Apple sample source code, but that you've made changes. Change History (most recent first): 8/31/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 */ #include "Interrupt_Routines.h" #include <Devices.h> /* This gets called when the requested read has completed. This is a good place to add any conversion routines to convert from your sound format into the format expected by the Sound Manager. It's important to note that the conversions I am doing here is to help show you how to do something more complicated. The Sound Manager 3.2 has an Endian converter ('sowt'), and you can specify if a sound is 'raw ' or 'twos' to have the Sound Manager do the conversion. We do the work ourselves for entertainment and education. */ /*-----------------------------------------------------------------------*/ #ifdef powerc pascal void ASoundFileCallBack (myParmBlkPtr passedPB) #else pascal void ASoundFileCallBack (myParmBlkPtr passedPB:__a0) #endif /*-----------------------------------------------------------------------*/ { long i = kInit; myParmBlkPtr myPB = nil; /* just in case a0 gets used for something else while we're here */ OSErr theErr = noErr; char dataFormat = kInit; #ifndef powerc long myA5; myA5 = SetA5 (passedPB->myA5); #endif if (passedPB == nil) { DebugPrint ("\pmyPB is nil!"); theErr = kNilPtrErr; } else { myPB = passedPB; if (myPB->pb.ioParam.ioResult == noErr) { if (myPB->theSoundInfo->doubleHeader.dbhSampleSize == kSixteen) { dataFormat += kIs16Bit; } if (myPB->theSoundInfo->doubleHeader.dbhNumChannels == kStereo) { dataFormat += kIsStereo; } if (myPB->theSoundInfo->needsMasking == true) { for (i = kInit; i <= myPB->pb.ioParam.ioActCount / sizeof (long); i++) { ((long *)myPB->pb.ioParam.ioBuffer)[i] ^= kLongMask; /* Convert from raw to 2's complement */ } } if (myPB->theSoundInfo->fileType == 'WAVE' && myPB->theSoundInfo->doubleHeader.dbhFormat == 'NONE') { /* We will have to rearange the data so that the Mac can play it properly */ switch (dataFormat) { case kMono8Bit: /* Do nothing (endian only effects words, not bytes) */ case kStereo8Bit: break; case kMono16Bit: /* Do endian conversion (i.e. 0x1234 5678 becomes 0x2143 6587) */ case kStereo16Bit: Endian16BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); break; default: DebugPrint ("\pBad header passed to ASoundFileCallBack"); } } if (ASoundIsBackwards (myPB->theSoundInfo) == true) { /* reverse the sound in the buffer so it sounds like we are playing backwards */ switch (dataFormat) { case kMono8Bit: ReverseMono8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); break; case kMono16Bit: ReverseMono16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); break; case kStereo8Bit: ReverseStereo8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); break; case kStereo16Bit: ReverseStereo16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); break; default: DebugPrint ("\pBad header passed to ASoundFileCallBack"); } ASoundPlayBackwards (myPB->theSoundInfo, false); } } else { DebugPrint("\pError in ASoundFileCallBack, ioResult was not zero!"); theErr = myPB->pb.ioParam.ioResult; } } if (theErr == noErr) { myPB->theBuffer->dbFlags |= dbBufferReady; } myPB->pbInUse = false; #ifndef powerc myA5 = SetA5 (myA5); #endif return; } /* This gets called when the Sound Manager has finished playing the buffer and wants you to refill it. Please note: It may look like this routine reuses the paramBlock, but it does not, there is a paramBlock for each buffer. This eliminates race conditions while priming the buffer, and makes sure that the paramBlock is not reused. You CANNOT reuse a paramBlock from within an ioCompletion routine! This will (ok, may) cause terrible problems, especially if you have File Sharing turned on. */ /*-----------------------------------------------------------------------*/ pascal void ASoundDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer) /*-----------------------------------------------------------------------*/ { #ifndef __SC__ #pragma unused (chan) #endif SoundInfoPtr theSoundInfo = nil; myParamBlockRec *myPB = nil; long bytesToCopy = kInit; OSErr theErr = noErr; #ifndef powerc long myA5; #endif theSoundInfo = (SoundInfoPtr)doubleBuffer->dbUserInfo[kSndInfoPtr]; if (IsValid (theSoundInfo) == false) { DebugPrint ("\pbad user field, dbUserInfo[0] is probably nil!"); theErr = kNilPtrErr; } else { myPB = (myParmBlkPtr)doubleBuffer->dbUserInfo[kPBPtr]; if (myPB == nil) { DebugPrint ("\pbad user field, myPB is nil!"); theErr = kNilPtrErr; } } #ifndef powerc myA5 = SetA5 (myPB->myA5); #endif /* The pbInUse field was added because of a race condition caused by the fact that the Sound Manager calls this routine one last time after the sound has stopped, and in some cases when the sound has stopped we want to read from the start of the file right away. This seems to fix the problem of file errors when stopping and starting a sound rapidly. If the PB is in use we just don't do the requested read. The pbInUse flag is cleared at the end of ASoundFileCallBack. */ if (theErr == noErr && myPB->pbInUse == false && theSoundInfo->stopping == false) { myPB->pb.ioParam.ioBuffer = (Ptr)doubleBuffer->dbSoundData; bytesToCopy = ASoundGetNumTotalBytes(theSoundInfo) - ASoundGetBytesCopied (theSoundInfo); if (bytesToCopy > ASoundGetBufferSize (theSoundInfo)) { bytesToCopy = ASoundGetBufferSize (theSoundInfo); } myPB->pb.ioParam.ioReqCount = bytesToCopy; myPB->pb.ioParam.ioPosOffset = ASoundGetBytesCopied (theSoundInfo); if (myPB->pb.ioParam.ioPosOffset < theSoundInfo->dataStart) { /* A little extra sanity checking */ myPB->pb.ioParam.ioPosOffset = theSoundInfo->dataStart; } myPB->theBuffer = doubleBuffer; /* Which buffer are we filling? */ if (bytesToCopy > kInit) { /* Do we really need to read more sound?*/ myPB->pbInUse = true; theErr = PBReadAsync(&myPB->pb); /* Do an async read of more sound */ } if (theErr >= noErr) { theErr = noErr; /* positive errors are not real errors */ theSoundInfo->bytesCopied += bytesToCopy; if (theSoundInfo->bytesPerFrame > kInit) { doubleBuffer->dbNumFrames = bytesToCopy / theSoundInfo->bytesPerFrame; } else { DebugPrint ("\pbytesPerFrame is a bad value!"); } if (theSoundInfo->bytesCopied == theSoundInfo->bytesTotal) doubleBuffer->dbFlags |= dbLastBuffer; theSoundInfo->currentBuffer++; } else { DebugPrint ("\pPBReadAsync error!"); } } #ifndef powerc (void)SetA5 (myA5); #endif return; } /* This gets called when the sound is finally done playing. It sets a flag that we check in our event loop because you can't clean up everything at interrupt time. */ /*-----------------------------------------------------------------------*/ pascal void ASoundDoneCallBack (SndChannelPtr chan, SndCommand *cmd) /*-----------------------------------------------------------------------*/ { #ifndef __SC__ #ifdef powerc #pragma unused (cmd) #endif #endif SoundInfoPtr theSoundInfo = nil; #ifndef powerc long myA5; myA5 = SetA5 (cmd->param2); #endif theSoundInfo = (SoundInfoPtr)chan->userInfo; if (StrictIsValid (theSoundInfo)) { theSoundInfo->soundDone = true; } else { DebugPrint ("\pbad sound channel pointer passed to ASoundDoneCallBack"); } #ifndef powerc myA5 = SetA5 (myA5); #endif return; }